﻿
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using Qianke.Core.Common;
using Qianke.Core.Common.Interface;
using Qianke.Core.Common.Models;
using System.Runtime.ExceptionServices;
using System.Security.Claims;
using System.Text.Encodings.Web;
using Volo.Abp.Auditing;

namespace Qianke.Core.Saas.WebApi.SaasExtensions.SaasAduthentication
{
    public class SaasTokenHandler : AuthenticationHandler<SaasTokenOptions>
    {
        private ReturnInfo Result = ReturnInfo.Success;
        private readonly SaasRequestInfo requestInfo;
        private readonly ITokenInfo tokenModel;


        public SaasTokenHandler(IOptionsMonitor<SaasTokenOptions> options, ILoggerFactory logger,
            UrlEncoder encoder,
            SaasRequestInfo requestInfo,
            ITokenInfo tokenModel)
            : base(options, logger, encoder)
        {
            this.requestInfo = requestInfo;
            this.tokenModel = tokenModel;
        }

        private string GetToken()
        {
            var token = Context.Request.Query["Token"].ToString();
            if (string.IsNullOrEmpty(token))
            {
                string text = Request.Headers.Authorization.ToString();
                if (!string.IsNullOrEmpty(text))
                {
                    if (text.StartsWith("SAASTOKEN ", StringComparison.OrdinalIgnoreCase))
                        token = text.Substring("SAASTOKEN ".Length).Trim();
                    else
                        token = text;
                }
            }
            if (string.IsNullOrEmpty(token))
            {
                if (!string.IsNullOrEmpty(requestInfo.RequstBody))
                {
                    try
                    {
                        if (requestInfo.RequstJToken != null)
                            token = requestInfo.RequstJToken["Token"].SafeToString();
                    }
                    catch (Exception)
                    {

                    }
                }
            }
            if (requestInfo.RequstJToken != null)
                requestInfo.RequstJToken["Token"] = token?.ToString();

            AsyncEnvironment.SetData("Token", token);
            return token ?? "";
        }


        private async Task FullRequestInfo()
        {
            Context.Request.EnableBuffering();
            var body = await new StreamReader(Context.Request.Body).ReadToEndAsync();
            Context.Request.Body.Seek(0, SeekOrigin.Begin);
            requestInfo.RequstBody = body;
            if (!string.IsNullOrEmpty(body))
            {
                try
                {
                    requestInfo.RequstJToken = body.ToObject<JToken>();
                }
                catch
                {
                    
                }
            }
            if (requestInfo.RequstJToken == null)
                requestInfo.RequstJToken = new JObject();
            foreach (var item in Request.Query)
                requestInfo.RequstJToken[item.Key] = item.Value.ToString();
        }

        private ReturnInfo FullTokenInfo()
        {
            //获取Token 1
            var token = GetToken();
            if (string.IsNullOrEmpty(token))
                return new ReturnInfo("501", "Token不能为空1");
            requestInfo.RequstJToken["Token"] = token;
            requestInfo.Token = token;
            ////Token 转模型
            var tokenType = tokenModel.GetType();
            ITokenInfo fullToken = null;
            try
            {
                fullToken = TokenHelper.TokenToModel(token, tokenType) as ITokenInfo;
            }
            catch (Exception ex)
            {
                return new ReturnInfo("502", "Token解析失败:" + ex.Message);
            }
            if (fullToken == null)
            {
                return new ReturnInfo("502", "Token解析失败");
            }
            foreach (var p in tokenType.GetProperties())
            {
                if (p.CanWrite)
                    p.SetValue(tokenModel, p.GetValue(fullToken));
            }
            requestInfo.SetTokenModel(tokenModel);

            AsyncEnvironment.SetData("TokenModel", tokenModel);
            return ReturnInfo.Success;
        }

        /// <summary>
        /// 填充日志模型
        /// </summary>
        private void FullAuditLogInfo()
        {
            var auditManager = Context.RequestServices.GetRequiredService<IAuditingManager>();
            var auditInfo = auditManager?.Current?.Log;
            if (auditInfo != null)
                auditInfo.ExtraProperties["ApiParamInfo"] = requestInfo.RequstJToken.ToJson();
            if (tokenModel.Id == 0)
                tokenModel.Id = requestInfo.RequstJToken["ManagerId"].ToInt64(0);
            if (tokenModel.OrganId == 0)
                tokenModel.OrganId = requestInfo.RequstJToken["OrganId"].ToInt64(0);
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            try
            {
                await FullRequestInfo();
                Result = FullTokenInfo();
                FullAuditLogInfo();
                if (Result.resultcode != "200")
                    return AuthenticateResult.NoResult();

                //解析token到Principal                
                var identity = new ClaimsIdentity();
                identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, tokenModel.Id.ToString()));
                identity.AddClaim(new Claim("OrganId", tokenModel.OrganId.ToString()));
                identity.AddClaim(new Claim(ClaimTypes.Expired, tokenModel.LastTime.ToString("yyyy-MM-dd HH:mm:ss")));

                var tokenValidatedContext = new SaasTokenValidatedContext(base.Context, base.Scheme, base.Options)
                {
                    Principal = new ClaimsPrincipal(identity)
                };
                tokenValidatedContext.Success();
                return tokenValidatedContext.Result;
            }
            catch (Exception ex)
            {
                ExceptionDispatchInfo.Capture(ex).Throw();
                throw;
            }
        }

        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Context.Response.StatusCode = StatusCodes.Status200OK;
            await Context.Response.WriteAsJsonAsync(Result);
        }
    }
}
